#include "EPoller.h"
#include "Channel.h"

#include <sys/epoll.h>
#include <unistd.h>
#include "util.h"
// #include <iostream>
#include "TcpConnection.h"

const int kNew = 0;
const int kExit = 1;

EPoller::EPoller(EventLoop* loop)
    : ownerLoop_(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
    events_(kInitEventListSize)
{
}

EPoller::~EPoller()
{
    ::close(epollfd_);
}

int EPoller::poll( int timeoutMs, ChannelList* activeChannels) {
    int numEvents = ::epoll_wait(epollfd_, events_.data(), static_cast<int>(events_.size()), timeoutMs);

    if (numEvents <= 0) return numEvents;

    fillActiveChannels(numEvents, activeChannels);
    
    if (static_cast<size_t>(numEvents) == events_.size()) {
        events_.resize(events_.size() * 2);
    }

    return numEvents;
}

void EPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const {
    for ( int i = 0; i < numEvents; ++i ) {
        Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
        channel->set_revents(events_[i].events);    //设置返回活动事件类型
        activeChannels->push_back(channel);
    }
}

// Channel中注册或注销关注事件时调用
void EPoller::updateChannel(Channel* channel) {
    const int index = channel->index();
    // 新Channel 添加
    if (index == kNew) {
        int fd = channel->fd();
        channels_[fd] = channel;
        channel->set_index(kExit);
        update(EPOLL_CTL_ADD, channel);
    }
    // index == kNew 已有
    else {
        int fd = channel->fd();
        update(EPOLL_CTL_MOD, channel);
    }
}

void EPoller::removeChannel(Channel* channel) {
    int fd = channel->fd();
    size_t n = channels_.erase(fd);
    if (n == 0) {
        cout << fd << " was not found\n";
    }
    update(EPOLL_CTL_DEL, channel);
    channel->destruct();
}

void EPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  bzero(&event, sizeof(event));
  event.events = channel->events();
  event.data.ptr = channel;
  int fd = channel->fd();
  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
  {
    // LOG_ERROR << "epoll_ctl op=" << operation << " fd=" << fd;
  }
}
